// PGLLinearNumbering.cpp: implementation of the CPGLLinearNumbering class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include <pgl/core/pgl.h>
#include <pgl/core/PGLArchive.h>
#include <pgl/core/PGLLinearNumbering.h>
#include <cmath>

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif


PGL_IMPLEMENT_DYNCREATE(CPGLLinearNumbering,CPGLNumbering);

#ifdef _DEBUG
void CPGLLinearNumbering::Dump( CDumpContext& dc ) const
{
    // call base class function first
    CPGLNumbering::Dump( dc );

    // now do the stuff for our specific class
	dc << _T("--- CPGLLinearNumbering ID ") << GetID() <<_T(" ---\n");
}

void CPGLLinearNumbering::AssertValid() const
{
    // call inherited AssertValid first
    CPGLNumbering::AssertValid();
} 
#endif

void CPGLLinearNumbering::SerializeXML(CPGLArchive& ar)
{
	static const TCHAR szTag[] = _T("CPGLLinearNumbering");

	markup::CMarkupArchive& xml = *((markup::CMarkupArchive*)ar.GetMarkup());
	if(ar.IsStoring())
	{
		xml.AddChildElem(szTag);
		xml.IntoElem();

			/////////////////////////////////////////////////////////
			// Adding base class
			CPGLNumbering::SerializeXML(ar);

			xml.AddChildElemEx( _T("Start"), m_dStart );
			xml.AddChildElemEx( _T("End"), m_dEnd );
			xml.AddChildElemEx( _T("Step"), m_dStep );
		xml.OutOfElem();
	}
	else
	{
		if  (!xml.FindChildElem(szTag))
				return;

		xml.IntoElem();

			/////////////////////////////////////////////////////////
			// loading base class
			CPGLNumbering::SerializeXML(ar);

			xml.FindGetChildData( _T("Start"), m_dStart );
			xml.FindGetChildData( _T("End"), m_dEnd );
			xml.FindGetChildData( _T("Step"), m_dStep );

		xml.OutOfElem();
	}
}

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CPGLLinearNumbering::CPGLLinearNumbering(ESide eSide)
: 
CPGLNumbering(eSide),
m_dStart(0),
m_dEnd(1),
m_iScale(0)
{

}

CPGLLinearNumbering::~CPGLLinearNumbering()
{

}

void CPGLLinearNumbering::ProcessLimits(
	double& dStart, 
	double& dEnd, 
	gfxinterface::CGfxInterface& gfx
	)
{
	using namespace algotools;

	m_dStart = dStart;
	m_dEnd = dEnd;

	GenerateSteps();

	dStart = GetStart();
	dEnd = GetEnd();
};

void CPGLLinearNumbering::GenerateSteps()
{
	double dStart=m_dStart;
	double dEnd=m_dEnd;
	
	int numberOfTicks = 10; // TODO: Should be computed so that no overlap exists
	int bases[]= {1,5,2}; // Tick bases selection
	int currentBase;
	int n;
	double intervalSize, upperBound, lowerBound;
    int nIntervals,nMaxIntervals;

	int exponentYmax;
	double mantissaYmax;
	double ip_start,ip_end;
	
	exponentYmax = static_cast<int>(
		floor( std::max( log10(fabs(dStart)), log10(fabs(dEnd)) ) )
		);
	mantissaYmax = dEnd/pow(10.0,exponentYmax);

	// now check if numbers can be cleaned...
	// make it pretty
	double significative_numbers=3;

	double expo = pow(10.0,  significative_numbers - exponentYmax);
	double start_norm = fabs(dStart) * expo;
	double end_norm = fabs(dEnd) * expo;
	double mant_norm = fabs(mantissaYmax) * expo;
	double r,ip;
		
	// trunc ends
	r= modf( start_norm, &ip);
	if ( r >= 0.5 )
		++ip;
	dStart=_copysign( ip, dStart);

	// trunc ends
	r= modf( end_norm, &ip);
	if ( r >= 0.5 )
		++ip;
	dEnd=_copysign( ip, dEnd);

	r= modf( mant_norm, &ip);
	mantissaYmax=_copysign( ip, mantissaYmax);

	nMaxIntervals=0;
	for (int k = 0; k <sizeof(bases)/sizeof(bases[0]); ++k)
	{
        // Loop initialisation
        currentBase = bases[k];
        n = 4; // This value only allows results smaller than about 1000 = 10^n
		
		
        do // Tick vector length reduction 
        {
			--n;
			intervalSize = currentBase*pow(10.0, exponentYmax-n);

 			upperBound = static_cast<int>(ceil(mantissaYmax*pow(10.0,n)/currentBase))*intervalSize;
			nIntervals = static_cast<int>(ceil((upperBound-dStart)/intervalSize));
			lowerBound = upperBound - nIntervals*intervalSize;
        }
        while ((nIntervals+1) > numberOfTicks);
        
        if (nIntervals > nMaxIntervals)
        {
			nMaxIntervals=nIntervals;
			ip_start=ip_start=lowerBound;
			ip_end=upperBound;
        }
	}

	// trunc ends
	start_norm = fabs(dStart);
	end_norm = fabs(dEnd);
	r= modf( start_norm, &ip_start);
	if ( r >= 0.5 )
		++ip;

	m_dStart=_copysign( ip_start / expo, m_dStart);

	// trunc ends
	r= modf( end_norm, &ip_end);
	if ( r > 0 )
		++ip;
	m_dEnd=_copysign( ip_end / expo, m_dEnd);

	// compute scale
	m_iScale=GetExponentScaling( m_dStart, m_dEnd, m_dNumberExpLimits);
	double dScale = pow(10.0,m_iScale);

	m_dStart/=dScale;
	m_dEnd/=dScale;

	m_dStep = fabs( (_copysign( ip_end, m_dEnd) - _copysign( ip_start, m_dEnd)) / (nMaxIntervals * expo) );

	if (algotools::IsGreaterEqual(m_dStart,m_dEnd)) // Flat plot
	{
		m_dStart=-0.5;
		m_dEnd=0.5;
		m_dStep=1.0/nMaxIntervals;
		m_iScale=0;
	}

}

int CPGLLinearNumbering::GetExponentScaling( 
	double dStart,
	double dEnd, 
	double dExpLimit)
{
	double dScale;
	double eStart, eEnd;

	dScale=0;
	eStart=log10(fabs(dStart));
	eEnd=log10(fabs(dEnd)) ;

	if (dEnd >0)
	{
		if ( fabs( eEnd ) > dExpLimit )
			dScale=eEnd/dExpLimit;
	}
	else
	{
		if ( fabs( eStart ) > dExpLimit )
			dScale=(eStart)/dExpLimit-1;
	}

	return static_cast<int>(floor(floor(dScale)*dExpLimit));
}

void CPGLLinearNumbering::PlotGfx(
	CPGLRectD const& region_rect_,
	CPGLRectD const& numbering_rect_,
	gfxinterface::CGfxInterface& gfx
	)
{
	using namespace gfxinterface;

	// Drawing...
	// setting color and font
	CPGLNumbering::PlotGfx(
		region_rect_,
		numbering_rect_,
		gfx
		);

	// creating vectors
	size_t nLabels=static_cast<size_t>(floor((m_dEnd-m_dStart)/m_dStep+ m_dStep*0.001)+1);
	std::vector<SPointD> vPoints(nLabels);
	std::vector<std::_tstring> vStrings(nLabels);
	
	CString str;
	size_t i;
	float x,y;
	SPointD str_size;
	double max_str_size(0);

	// Creating strings...
	if (IsHorizontal())
	{
		x=static_cast<float>(m_dStart);
		y=static_cast<float>(
			  m_dStart 
			- gfx.BBToWorldAbsy( m_text.GetPixelAnchorToTop() + 5 ) 
			+ (m_dEnd - m_dStart)
			);
	}
	else
	{
		y=static_cast<float>(m_dStart);
		x=static_cast<float>(
			  m_dStart 
			- gfx.BBToWorldAbsx( m_text.GetPixelAnchorToTop() + 5 ) 
			+ (m_dEnd - m_dStart)
			);
	}

	// creating labels
	for (i=0;i<nLabels;++i)
	{
		// checking x
		if (fabs(x) < m_dStep*1e-3)
			x=0;
		if (fabs(y) < m_dStep*1e-3)
			y=0;

		str.Format(m_sNumberFormat,x);

		vPoints[i].x=x;
		vPoints[i].y=y;
		vStrings[i]=str;
		// updating
		if (IsHorizontal())
			x+=static_cast<float>(m_dStep);
		else
			y+=static_cast<float>(m_dStep);
	}

	// changing string if scaling present...
	if ( m_iScale != 0)
	{
		str.Format(_T("x10^%d"), m_iScale);

		vPoints.resize(nLabels+1);
		if (IsHorizontal())
			vPoints.push_back(
					SPointD( vPoints[nLabels-1].x - m_dStep/2.0,
						y )
					);
		else
			vPoints.push_back(
					SPointD( x,
						vPoints[nLabels-1].y - m_dStep/2.0 )
					);
		vStrings.push_back( (LPCTSTR)str );
	}

	// drawing
	gfx.DrawTextStrip(vPoints, vStrings, m_text.GetAlign(), m_text.GetAngle());
}


void CPGLLinearNumbering::ComputeExtent(CPGLRectD& r)
{
	using namespace gfxinterface;

	// creating vectors
	size_t nLabels=static_cast<size_t>(
		floor((m_dEnd-m_dStart)/m_dStep+ m_dStep*0.001)+1
		);	
	CString str;
	size_t i;
	SPointD str_size;
	double max_str_size(0),x(0),y(0);

	// creating labels
	for (i=0;i<nLabels;++i)
	{
		// checking x
		if (fabs(x) < m_dStep*1e-3)
			x=0;
		if (fabs(y) < m_dStep*1e-3)
			y=0;

		str.Format(m_sNumberFormat,x);
		GetText().SetString( str );

		// updating
		if (IsHorizontal())
		{
			x+=static_cast<float>(m_dStep);
			max_str_size = std::max( max_str_size, GetText().GetPixelHeight() );
		}
		else
		{
			y+=static_cast<float>(m_dStep);
			max_str_size = std::max( max_str_size, GetText().GetPixelWidth() );
		}
	}

	// changing string if scaling present...
	if ( m_iScale != 0)
	{
		str.Format(_T("x10^%d"), m_iScale);
		if (IsHorizontal())
			max_str_size = std::max( max_str_size, GetText().GetPixelHeight() );
		else
			max_str_size = std::max( max_str_size, GetText().GetPixelWidth() );
	}

	if (IsHorizontal())
	{
		r.left=m_dStart;
		r.right=m_dEnd;
		r.bottom=0;
		r.top=max_str_size;
	}
	else
	{
		r.left=0;
		r.right=max_str_size;
		r.bottom=GetStart();
		r.top=GetEnd();
	}

}
